package concurrency;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import net.mindview.util.Generated;
import net.mindview.util.RandomGenerator;

abstract class Tester<C> {
    protected C mTestContainer;
    
    private CountDownLatch mEndLatch;
    
    protected int mReaders;
    
    protected int mWriters;
    
    private String mTestId;
    
    protected volatile long mReadTime = 0;
    
    protected volatile long mWriteTime = 0;
    
    protected volatile long mReadResult = 0;
    
    protected Integer[] mWriteDatas;
    
    protected static int sContainerSize = 1000;
    
    protected static ExecutorService sExec = Executors.newCachedThreadPool();
    
    /**
     * test repetition
     */
    private static int sTestReps = 10;
    
    protected static int sTestCycles = 1000;
    
    /* package */ Tester(String testId, int readers, int writers) {
        mTestId = testId + " " + readers + "r " + writers + "w";
        mReaders = readers;
        mWriters = writers;
        RandomGenerator.Integer generator = new RandomGenerator.Integer();
        mWriteDatas = Generated.array(Integer.class, generator, sContainerSize);

        for (int i = 0; i < sTestReps; i++) {
            runTest();
            mReaders = 0;
            mWriters = 0;
        }
    }
    
    /* package */ abstract C containerInitializer();
    
    /* package */ abstract void startReadersAndWriters();
    
    /* package */ void runTest() {
        mEndLatch = new CountDownLatch(mReaders + mWriters);
        mTestContainer = containerInitializer();
        startReadersAndWriters();
        try {
            mEndLatch.await();
        } catch (InterruptedException e) {
            System.out.println("endLatch interrupted");
            e.printStackTrace();
        }
        
        System.out.printf("%-27s %14d %14d\n", mTestId, mReadTime, mWriteTime);
        
        if (mReadTime != 0 && mWriteTime != 0) {
            System.out.printf("%-27s %14d\n", "readTime + writeTime =", mReadTime + mWriteTime);
        }
    }
    
    abstract class TestTask implements Runnable {
        protected long mDuration;
        
        abstract void test();
        
        abstract void putResults();
        
        @Override
        public void run() {
            long startTime = System.nanoTime();
            test();
            mDuration = System.nanoTime() - startTime;
            synchronized (Tester.this) {
                putResults();
            }
            mEndLatch.countDown();
        }
    }
    
    public static void initMain(String[] args) {
        if(args.length > 0) {
            sTestReps = new Integer(args[0]);
        }
        
        if(args.length > 1) {
            sTestCycles = new Integer(args[1]);
        }
        
        if(args.length > 2) {
            sContainerSize = new Integer(args[2]);
        }      
        
        System.out.printf("%-27s %14s %14s\n", "Type", "Read time", "Write time");
    }
}
